﻿using BBDown.Core.Protobuf;
using Google.Protobuf;
using System.Text;
using static BBDown.Core.Entity.Entity;
using static BBDown.Core.Util.HTTPUtil;
using System.Text.RegularExpressions;
using System.Text.Json;

namespace BBDown.Core.Util
{
    public partial class SubUtil
    {
        //https://i0.hdslb.com/bfs/subtitle/subtitle_lan.json
        public static (string, string) GetSubtitleCode(string key)
        {
            //zh-hans => zh-Hans
            if (NonCapsRegex().Match(key) is { Success: true } result)
            {
                var v = result.Value;
                key = key.Replace(v, v.ToUpper());
            }

            return key switch
            {
                "ai-Zh"             => ("chi", "中文（简体, AI识别）"),
                "ai-En"             => ("eng", "English(generated by ai)"),
                "zh-CN"             => ("chi", "中文（简体）"),
                "zh-HK"             => ("chi", "中文（香港繁體）"),
                "zh-Hans"           => ("chi", "中文（简体）"),
                "zh-TW"             => ("chi", "中文（台灣繁體）"),
                "zh-Hant"           => ("chi", "中文（繁體）"),
                "en-US"             => ("eng", "English(USA)"),
                "ja"                => ("jpn", "日本語"),
                "ko"                => ("kor", "한국어"),
                "zh-SG"             => ("chi", "中文（新加坡）"),
                "ab"                => ("abk", "Аҳәынҭқарра"),
                "aa"                => ("aar", "Qafár af"),
                "af"                => ("afr", "Afrikaans"),
                "sq"                => ("alb", "Gjuha shqipe"),
                "ase"               => ("ase", "American Sign Language"),
                "am"                => ("amh", "አማርኛ"),
                "arc"               => ("arc", "ܐܪܡܝܐ"),
                "hy"                => ("arm", "հայերեն"),
                "as"                => ("asm", "অসমীয়া"),
                "ay"                => ("aym", "Aymar aru"),
                "az"                => ("aze", "Azərbaycan"),
                "bn"                => ("ben", "বাংলা ভাষার"),
                "ba"                => ("bak", "Башҡорттеле"),
                "eu"                => ("baq", "Euskara"),
                "be"                => ("bel", "беларуская мова biełaruskaja mova"),
                "bh"                => ("bih", "Bihar"),
                "bi"                => ("bis", "Bislama"),
                "bs"                => ("bos", "босански"),
                "br"                => ("bre", "Breton"),
                "bg"                => ("bul", "български"),
                "yue"               => ("chi", "粵語"),
                "yue-HK"            => ("chi", "粵語（中國香港）"),
                "ca"                => ("cat", "català"),
                "chr"               => ("chr", "ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ"),
                "cho"               => ("cho", "Chahta'"),
                "co"                => ("cos", "lingua corsa"),
                "hr"                => ("hrv", "Hrvatska"),
                "cs"                => ("cze", "čeština"),
                "da"                => ("dan", "Dansk"),
                "nl"                => ("dut", "Nederlands"),
                "nl-BE"             => ("dut", "Nederlands(Belgisch)"),
                "nl-NL"             => ("dut", "Nederlands(Nederlands)"),
                "dz"                => ("dzo", "རྫོང་ཁ།"),
                "en"                => ("eng", "English"),
                "en-CA"             => ("eng", "English(Canada)"),
                "en-IE"             => ("eng", "English(Ireland)"),
                "en-GB"             => ("eng", "English(UK)"),
                "eo"                => ("epo", "Esperanto"),
                "et"                => ("est", "Eestlane"),
                "fo"                => ("fao", "føroyskt"),
                "fj"                => ("fij", "Vakaviti"),
                "fil"               => ("phi", "Pilipino"),
                "fi"                => ("fin", "Suomi"),
                "fr"                => ("fre", "Français"),
                "fr-BE"             => ("fre", "Français(Belgique)"),
                "fr-CA"             => ("fre", "Français(Canada)"),
                "fr-FR"             => ("fre", "Français(La France)"),
                "fr-CH"             => ("fre", "Français(Suisse)"),
                "ff"                => ("ful", "Fulani"),
                "gl"                => ("glg", "galego"),
                "ka"                => ("geo", "ქართული ენა"),
                "de"                => ("ger", "Deutsch"),
                "de-AT"             => ("ger", "Deutsch(Österreich)"),
                "de-DE"             => ("ger", "Deutsch(Deutschland)"),
                "de-CH"             => ("ger", "Deutsch(Schweiz)"),
                "el"                => ("gre", "Ελληνικά"),
                "kl"                => ("kal", "Kalaallisut"),
                "gn"                => ("grn", "avañe'ẽ"),
                "gu"                => ("guj", "ગુજરાતી"),
                "hak"               => ("hak", "Hak-kâ-fa"),
                "hak-TW"            => ("hak", "Hak-kâ-fa"),
                "ha"                => ("hau", "هَوُسَ"),
                "iw"                => ("heb", "שפה עברית"),
                "hi"                => ("hin", "हिन्दी"),
                "hi-Latn"           => ("hin", "हिंदी(फोनेटिक)"),
                "hu"                => ("hun", "Magyar"),
                "is"                => ("ice", "icelandic"),
                "ig"                => ("ibo", "Asụsụ Igbo"),
                "id"                => ("ind", "Indonesia"),
                "ia"                => ("ina", "Interlingua"),
                "iu"                => ("iku", "ᐃᓄᒃᑎᑐᑦ"),
                "ik"                => ("ipk", "Inupiat"),
                "ga"                => ("gle", "Gaeilge na hÉireann"),
                "it"                => ("ita", "Italiano"),
                "jv"                => ("jav", "ꦧꦱꦗꦮ"),
                "kn"                => ("kan", "ಕನ್ನಡ"),
                "ks"                => ("kas", "कॉशुर"),
                "kk"                => ("kaz", "Қазақ тілі"),
                "km"                => ("khm", "ភាសាខ្មែរ"),
                "rw"                => ("kin", "Ikinyarwanda"),
                "tlh"               => ("tlh", "tlhIngan Hol"),
                "ku"                => ("kur", "Kurdî"),
                "ky"                => ("kir", "кыргыз тили"),
                "lo"                => ("lao", "ພາສາລາວ"),
                "la"                => ("lat", "latīna"),
                "lv"                => ("lav", "latviešu valoda"),
                "ln"                => ("lin", "Lingála"),
                "lt"                => ("lit", "lietuvių kalba"),
                "lb"                => ("ltz", "Lëtzebuergesch"),
                "mk"                => ("mac", "Македонски јазик"),
                "mg"                => ("mlg", "maa.laa.gaas"),
                "ms"                => ("may", "Melayu"),
                "ml"                => ("mal", "മലയാളം"),
                "mt"                => ("mlt", "Lingwa Maltija"),
                "mi"                => ("mao", "Māori"),
                "mr"                => ("mar", "मराठी Marāṭhī"),
                "mas"               => ("mas", "Maasai"),
                "nan"               => ("nan", "閩南語"),
                "nan-TW"            => ("nan", "閩南語(台灣)"),
                "lus"               => ("lus", "Mizo ṭawng"),
                "mo"                => ("mol", "Limba moldovenească"),
                "mn"                => ("mon", "монгол хэл"),
                "my"                => ("bur", "မြန်မာဘာသာ"),
                "na"                => ("nau", "Dorerin Naoero"),
                "nv"                => ("nav", "Diné bizaad"),
                "ne"                => ("nep", "नेपाली Nepālī"),
                "no"                => ("nor", "norsk språk"),
                "fa"                => ("per", "فارسی"),
                "fa-AF"             => ("per", "فارسی"),
                "fa-IR"             => ("per", "فارسی"),
                "pl"                => ("pol", "Polski"),
                "pt"                => ("por", "Português"),
                "pt-BR"             => ("por", "Português(brasil)"),
                "pt-PT"             => ("por", "Português(portugal)"),
                "ro"                => ("rum", "Română"),
                "ru"                => ("rus", "Русский"),
                "ru-Latn"           => ("rus", "Русский(фонетический)"),
                "sr"                => ("srp", "Српски"),
                "sr-Cyrl"           => ("srp", "Српски(ћирилица)"),
                "sr-Latn"           => ("srp", "Српски(латиница)"),
                "sh"                => ("scr", "srpskohrvatski"),
                "sk"                => ("slo", "slovenský"),
                "es"                => ("spa", "Español"),
                "es-419"            => ("spa", "Español(Latinoamérica)"),
                "es-MX"             => ("spa", "Español(México)"),
                "es-ES"             => ("spa", "Español(España)"),
                "es-US"             => ("spa", "Español(Estados Unidos)"),
                "sv"                => ("swe", "Svenska"),
                "tl"                => ("tgl", "Tagalog"),
                "th"                => ("tha", "ไทย"),
                "tr"                => ("tur", "Türkçe"),
                "uk"                => ("ukr", "Українська"),
                "ur"                => ("urd", "Urdu"),
                "vi"                => ("vie", "Tiếng Việt"),
                //太多了，我蚌埠住了，后面懒得查
                //"ie"                => ("", ""),
                //"oc"                => ("",   ""),
                //"or"                => ("",   ""),
                //"om"                => ("",   ""),
                //"ps"                => ("",   ""),
                //"pa"                => ("",   ""),
                //"qu"                => ("",   ""),
                //"rm"                => ("",   ""),
                //"rn"                => ("",   ""),
                //"sm"                => ("",   ""),
                //"sg"                => ("",   ""),
                //"sa"                => ("",   ""),
                //"gd"                => ("",   ""),
                //"sdp"               => ("",   ""),
                //"sn"                => ("",   ""),
                //"scn"               => ("",   ""),
                //"sd"                => ("",   ""),
                //"si"                => ("",   ""),
                //"sl"                => ("",   ""),
                //"so"                => ("",   ""),
                //"st"                => ("",   ""),
                //"su"                => ("",   ""),
                //"sw"                => ("",   ""),
                //"ss"                => ("",   ""),
                //"tg"                => ("",   ""),
                //"ta"                => ("",   ""),
                //"tt"                => ("",   ""),
                //"te"                => ("",   ""),
                //"ti"                => ("",   ""),
                //"to"                => ("",   ""),
                //"ts"                => ("",   ""),
                //"tn"                => ("",   ""),
                //"tk"                => ("",   ""),
                //"tw"                => ("",   ""),
                //"uz"                => ("",   ""),
                //"vo"                => ("",   ""),
                //"cy"                => ("",   ""),
                //"fy"                => ("",   ""),
                //"wo"                => ("",   ""),
                //"xh"                => ("",   ""),
                //"yi"                => ("",   ""),
                //"yo"                => ("",   ""),
                //"zu"                => ("",   ""),
                _ => ("und", "Undetermined")
            };
        }

        #region 字幕接口

        private static async Task<List<Subtitle>?> GetIntlSubtitlesFromApi1Async(string aid, string cid, string epId, int index)
        {
            try
            {
                List<Subtitle> subtitles = new();
                string api = "https://" + (Config.EPHOST == "api.bilibili.com" ? "api.biliintl.com" : Config.EPHOST) + $"/intl/gateway/web/v2/subtitle?episode_id={epId}";
                string json = await GetWebSourceAsync(api);
                using var infoJson = JsonDocument.Parse(json);
                var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitles").EnumerateArray();
                foreach (var sub in subs)
                {
                    var lan = sub.GetProperty("lang_key").ToString();
                    var url = sub.GetProperty("url").ToString();
                    Subtitle subtitle = new()
                    {
                        url = url,
                        lan = lan,
                        path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
                    };

                    //有空的URL 不合法
                    if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                        throw new Exception("Bad url");

                    subtitles.Add(subtitle);
                }
                return subtitles;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static async Task<List<Subtitle>?> GetIntlSubtitlesFromApi2Async(string aid, string cid, string epId, int index)
        {
            try
            {
                List<Subtitle> subtitles = new();
                string api = "https://" + (Config.HOST == "api.bilibili.com" ? "api.bilibili.tv" : Config.HOST) +
                            $"/intl/gateway/v2/ogv/view/app/season?ep_id={epId}&platform=android&s_locale=zh_SG" + (Config.TOKEN != "" ? $"&access_key={Config.TOKEN}" : "");
                string json = await GetWebSourceAsync(api);
                using var infoJson = JsonDocument.Parse(json);
                var subs = infoJson.RootElement.GetProperty("result").GetProperty("modules")[0].GetProperty("data")
                    .GetProperty("episodes")[index - 1].GetProperty("subtitles").EnumerateArray();
                foreach (var sub in subs)
                {
                    var lan = sub.GetProperty("key").ToString();
                    var url = sub.GetProperty("url").ToString().Replace("\\\\/", "/");
                    Subtitle subtitle = new()
                    {
                        url = url,
                        lan = lan,
                        path = $"{aid}/{aid}.{cid}.{lan}{(url.Contains(".json") ? ".srt" : ".ass")}"
                    };

                    //有空的URL 不合法
                    if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                        throw new Exception("Bad url");

                    subtitles.Add(subtitle);
                }
                return subtitles;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static async Task<List<Subtitle>?> GetSubtitlesFromApi1Async(string aid, string cid, string epId, int index)
        {
            try
            {
                List<Subtitle> subtitles = new();
                string api = $"https://api.bilibili.com/x/web-interface/view?aid={aid}&cid={cid}";
                string json = await GetWebSourceAsync(api);
                using var infoJson = JsonDocument.Parse(json);
                var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitle").GetProperty("list").EnumerateArray();
                foreach (var sub in subs)
                {
                    var lan = sub.GetProperty("lan").ToString();
                    Subtitle subtitle = new()
                    {
                        url = sub.GetProperty("subtitle_url").ToString(),
                        lan = lan,
                        path = $"{aid}/{aid}.{cid}.{lan}.srt"
                    };
                    subtitles.Add(subtitle);
                }

                //有空的URL 不合法
                if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                    throw new Exception("Bad url");

                //无字幕片源 但是字幕没上导致的空列表，尝试从国际接口获取
                //if (subtitles.Count == 0 && !string.IsNullOrEmpty(epId))
                //{
                //    return await GetSubtitlesAsync(aid, cid, epId, true);
                //}
                return subtitles;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static async Task<List<Subtitle>?> GetSubtitlesFromApi2Async(string aid, string cid, string epId, int index)
        {
            try
            {
                List<Subtitle> subtitles = new();
                string api = $"https://api.bilibili.com/x/player/v2?cid={cid}&aid={aid}";
                string json = await GetWebSourceAsync(api);
                using var infoJson = JsonDocument.Parse(json);
                var subs = infoJson.RootElement.GetProperty("data").GetProperty("subtitle").GetProperty("subtitles").EnumerateArray();
                foreach (var sub in subs)
                {
                    var lan = sub.GetProperty("lan").ToString();
                    Subtitle subtitle = new()
                    {
                        url = sub.GetProperty("subtitle_url").ToString(),
                        lan = lan,
                        path = $"{aid}/{aid}.{cid}.{lan}.srt"
                    };
                    subtitles.Add(subtitle);
                }

                //有空的URL 不合法
                if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                    throw new Exception("Bad url");

                return subtitles;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static byte[] GetPayload(long aid, long cid)
        {
            var obj = new DmViewReq
            {
                Pid = aid,
                Oid = cid,
                Type = 1,
                Spmid = "main.ugc-video-detail.0.0",
            };
            return AppHelper.PackMessage(obj.ToByteArray());
        }

        private static async Task<List<Subtitle>?> GetSubtitlesFromApi3Async(string aid, string cid, string epId, int index)
        {
            try
            {
                List<Subtitle> subtitles = new();
                //grpc调用接口 protobuf
                string api = "https://app.biliapi.net/bilibili.community.service.dm.v1.DM/DmView";

                var data = GetPayload(Convert.ToInt64(aid), Convert.ToInt64(cid));

                var t = AppHelper.ReadMessage(await GetPostResponseAsync(api, data));
                var resp = new MessageParser<DmViewReply>(() => new DmViewReply()).ParseFrom(t);

                if (resp.Subtitle != null && resp.Subtitle.Subtitles != null)
                {
                    subtitles.AddRange(resp.Subtitle.Subtitles.Select(item => new Subtitle() {
                        url = item.SubtitleUrl,
                        lan = item.Lan,
                        path = $"{aid}/{aid}.{cid}.{item.Lan}.srt"
                    }));
                }
                //有空的URL 不合法
                if (subtitles.Any(s => string.IsNullOrEmpty(s.url)))
                    throw new Exception("Bad url");

                return subtitles;
            }
            catch (Exception)
            {
                return null;
            }
        }

        #endregion

        public static async Task<List<Subtitle>> GetSubtitlesAsync(string aid, string cid, string epId, int index, bool intl)
        {
            List<Subtitle>? subtitles = new();
            if (intl)
            {
                subtitles = await GetIntlSubtitlesFromApi1Async(aid, cid, epId, index) ?? await GetIntlSubtitlesFromApi2Async(aid, cid, epId, index);
            }
            else
            {
                if (Config.COOKIE == "")
                {
                    subtitles = await GetSubtitlesFromApi3Async(aid, cid, epId, index); // 未登录只有APP可以拿到字幕了
                }
                else
                {
                    subtitles = await GetSubtitlesFromApi2Async(aid, cid, epId, index)
                        ?? await GetSubtitlesFromApi1Async(aid, cid, epId, index)
                        ?? await GetSubtitlesFromApi3Async(aid, cid, epId, index);
                }

            }

            if (subtitles == null)
            {
                return new List<Subtitle>(); //返回空列表
            }

            //修正 url 协议
            foreach (var item in subtitles)
            {
                if (item.url.StartsWith("//")) item.url = "https:" + item.url;
            }

            return subtitles;
        }

        public static async Task SaveSubtitleAsync(string url, string path)
        {
            if (path.EndsWith(".srt"))
                await File.WriteAllTextAsync(path, ConvertSubFromJson(await GetWebSourceAsync(url)), Encoding.UTF8);
            else
                await File.WriteAllTextAsync(path, await GetWebSourceAsync(url), Encoding.UTF8);
        }

        private static string ConvertSubFromJson(string jsonString)
        {
            StringBuilder lines = new();
            var json = JsonDocument.Parse(jsonString);
            var sub = json.RootElement.GetProperty("body").EnumerateArray().ToList();
            for(int i = 0; i < sub.Count; i++)
            {
                var line = sub[i];
                lines.AppendLine((i + 1).ToString());
                if (line.TryGetProperty("from", out JsonElement from))
                {
                    lines.AppendLine($"{FormatTime(from.GetDouble())} --> {FormatTime(line.GetProperty("to").GetDouble())}");
                }
                else
                {
                    lines.AppendLine($"{FormatTime(0.0)} --> {FormatTime(line.GetProperty("to").GetDouble())}");
                }
                //有的没有内容
                if (line.TryGetProperty("content", out JsonElement content))
                    lines.AppendLine(content.ToString());
                lines.AppendLine();
            }
            return lines.ToString();
        }

        private static string FormatTime(double sec) //64.13
        {
            return TimeSpan.FromSeconds(sec).ToString(@"hh\:mm\:ss\,fff");
        }

        [GeneratedRegex("-[a-z]")]
        private static partial Regex NonCapsRegex();
    }
}
